package de.g18.it0a.jlaser.domain;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import de.g18.it0a.jlaser.exception.IllegalPositionException;
import de.g18.it0a.jlaser.factory.TileFactory;


public class Room {

    private final RoomConfiguration config;
    private final Vector<Tile> tiles;
    private final Vector<Mirror> mirrors;


    public Room(RoomConfiguration aConfig) {
        config = aConfig;
        tiles = new Vector<Tile>();
        mirrors = new Vector<Mirror>();
        createTiles();
    }

    private void createTiles() {
        if (!tiles.isEmpty()) {
            return;
        }

        for (int x = 0; x < getConfig().getWidth(); x += getConfig().getTileWidth()) {
            for (int y = 0; y < getConfig().getHeight(); y += getConfig().getTileHeight()) {
                Position pos = new Position(x, y);
                tiles.add(TileFactory.create(pos, getConfig()));
            }
        }
    }

    public RoomConfiguration getConfig() {
        return config;
    }

    public Set<Tile> getTilesBetween(Position aFirst, Position aSecond) {
        Set<Tile> result = new HashSet<Tile>();
        int minX = Math.min(aFirst.getX(), aSecond.getX());
        int maxX = Math.max(aFirst.getX(), aSecond.getX());
        int minY = Math.min(aFirst.getY(), aSecond.getY());
        int maxY = Math.max(aFirst.getY(), aSecond.getY());

        for (int x = minX; x < maxX; x += getConfig().getTileWidth()) {
            for (int y = minY; y < maxY; y += getConfig().getTileHeight()) {
                Position tilePosition = new Position(x, y);
                try {
                    result.add(getTileAt(tilePosition));
                } catch (IllegalPositionException e) {
                    // Sollte nicht passieren!
                    throw new IllegalStateException(tilePosition.toString() + " is not a valid tile position!", e);
                }
            }
        }
        return result;
    }

    public List<Tile> getTiles() {
        return tiles;
    }

    public Laser getLaser() {
        for (Tile tile : getTiles()) {
            for (Fixture fixture : tile.getFixtures()) {
                if (fixture instanceof Laser) {
                    return (Laser) fixture;
                }
            }
        }
        return null;
    }

    public Vector<Mirror> getMirrors() {
        return mirrors;
    }

    public List<Mirror> getSortedUsedMirrors() {
        List<Mirror> sortedMirrors = new ArrayList<Mirror>();
        if (getLaser() != null) {
            Position possibleMirrorPosition = getLaser().getPointedPosition();
            while (possibleMirrorPosition != null) {
                Mirror m = getMirrorAt(possibleMirrorPosition);
                if (m != null) {
                    if (sortedMirrors.contains(m)) {
                        System.out.println("Loop detected (" + m + " has been previously added), return!");
                        return sortedMirrors;
                    }
                    sortedMirrors.add(m);
                    possibleMirrorPosition = m.getPointedPosition();
                } else {
                    possibleMirrorPosition = null;
                }
            }
        }
        return sortedMirrors;
    }

    public Mirror getMirrorAt(Position aPosition) {
//        Tile tile = getTileAt(aPosition);
//        if (tile != null) {
//            for (Fixture fixture : tile.getFixtures()) {
//                if (fixture instanceof Mirror) {
//                    return (Mirror) fixture;
//                }
//            }
//        }
        for (Mirror mirror : mirrors) {
            if (mirror.getPosition().equals(aPosition)) {
                return mirror;
            }
        }
        return null;
    }

    public void addFixture(Fixture aFixture) throws IllegalPositionException {
        if (aFixture instanceof Mirror) {
            mirrors.add((Mirror) aFixture);
        }
        getTileAt(aFixture.getPosition()).addFixture(aFixture);
    }

    public Tile getTileAt(Position aPosition) throws IllegalPositionException {
        Tile result = null;
        for (Tile tile : tiles) {
            if (aPosition.getX() >= tile.getPosition().getX()
                 && aPosition.getX() <= tile.getPosition().getX() + getConfig().getTileWidth()
                 && aPosition.getY() >= tile.getPosition().getY()
                 && aPosition.getY() <= tile.getPosition().getY() + getConfig().getTileHeight()) {
                result = tile;
            }
        }

        if (result == null) {
            throw new IllegalPositionException(aPosition + " does not represent a valid position in the room!", aPosition);
        }
        return result;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (config == null ? 0 : config.hashCode());
        result = prime * result + (mirrors == null ? 0 : mirrors.hashCode());
        result = prime * result + (tiles == null ? 0 : tiles.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object aObject) {
        if (aObject == this) {
            return true;
        }
        if (aObject == null) {
            return false;
        }
        if (!(aObject instanceof Room)) {
            return false;
        }

        Room other = (Room) aObject;
        return getConfig().equals(other.getConfig())
                && getTiles().equals(other.getTiles());
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "[configuration=" + getConfig()
                                          + ",tiles=" + getTiles().size() + "]";
    }
}
